package scenebuilders;

import java.awt.Component;
import java.net.URL;
import java.util.LinkedList;

import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.Group;
import javax.media.j3d.Material;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Texture;
import javax.media.j3d.TextureAttributes;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Color3f;
import javax.vecmath.Color4f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.geometry.Box;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.image.TextureLoader;

public class SceneBuilder
{
	Group sceneRoot;
	LinkedList<TransformGroup> tgs = new LinkedList<TransformGroup>();
	public ShapeBuilder shapeBuilder = new ShapeBuilder(); 
	Appearance appearance = new Appearance();
	Transform3D transform = new Transform3D();
	Transform3D trnfrmTmp = new Transform3D();
	Component component;
			
	public SceneBuilder(Group sceneRoot, Component component) {
		this.sceneRoot = sceneRoot;
		this.component = component;
		//this.useColor(1,1,1);
		transform.setIdentity();
	}
	
	public SceneBuilder moveToX(float cor) {
		this.moveTo(cor,0,0);
		return this;
	}
	
	public SceneBuilder moveToY(float cor) {
		this.moveTo(0,cor,0);		
		return this;
	}

	public SceneBuilder moveToZ(float cor) {
		this.moveTo(0,0,cor);
		return this;
	}

	public SceneBuilder moveTo(float x, float y, float z) {
		moveTo(new Vector3f(x,y,z));
		return this;
	}
	
	public SceneBuilder moveTo(Vector3f v3f) {
		transform.setTranslation(v3f);
		return this;
	}	
	
	public SceneBuilder rotX(double angle) {
		trnfrmTmp.rotX(angle);
		transform.mul(trnfrmTmp);
		return this;
	}

	public SceneBuilder rotY(double angle) {
		trnfrmTmp.rotY(angle);
		transform.mul(trnfrmTmp);
		return this;
	}

	public SceneBuilder rotZ(double angle) {
		trnfrmTmp.rotZ(angle);
		transform.mul(trnfrmTmp);
		return this;
	}
					
	public void addCube(float width) {
		addCube(width, width, width);
	}
	
	public void addCube(float wx, float wy, float wz) {
		int primflags = Box.GENERATE_NORMALS + Box.GENERATE_TEXTURE_COORDS;
		Appearance app_clone = (Appearance) appearance.cloneNodeComponent(true);
		Box box = new Box(wx, wy, wz, primflags, app_clone);
		TransformGroup objTrans = CreateTransformGroup();
		objTrans.addChild(box);
	}
	
	public void addSphere(float radius) {
		int primflags = Sphere.GENERATE_NORMALS + Sphere.GENERATE_TEXTURE_COORDS;
		Appearance app_clone = (Appearance) appearance.cloneNodeComponent(true);
		Sphere sphere = new Sphere(radius, primflags, app_clone);
		TransformGroup objTrans = CreateTransformGroup();
		objTrans.addChild(sphere);
	}
	
	public void addRectPlane(float sirka, float vyska) {
		Shape3D plane = shapeBuilder.CreateRectPlane(sirka, vyska);
		Appearance app_clone = (Appearance) appearance.cloneNodeComponent(true);
		TransformGroup objTrans = CreateTransformGroup();
		plane.setAppearance(app_clone);
		objTrans.addChild(plane);
	}	
	
	public void addShape3d(Shape3D node) {
		Appearance app_clone = (Appearance) appearance.cloneNodeComponent(true);
		TransformGroup objTrans = CreateTransformGroup();
		node.setAppearance(app_clone);
		objTrans.addChild(node);
	}
	
	public SceneBuilder createTG() {
		TransformGroup objTrans = CreateTransformGroup();
		tgs.add(objTrans);
		return this;
	}
	
	public SceneBuilder removeTG(boolean all) {
		if (all)
			tgs.clear();
		else
			tgs.removeLast();
		return this;
	}	
	
	private TransformGroup CreateTransformGroup() {
		Transform3D tclone = new Transform3D(transform);
		TransformGroup objTrans = new TransformGroup(tclone);
		if (tgs.size() > 0)
			tgs.getLast().addChild(objTrans);
		else
			sceneRoot.addChild(objTrans);
		transform.setIdentity();
		return objTrans;
	}
			
	//////////////////////////////////////////////////////////////////////////////////
	// appearance part 
	//////////////////////////////////////////////////////////////////////////////////	
					
	public SceneBuilder useTexture(URL textImage) {
		// Set up the texture map		    
		Texture texture = new TextureLoader(textImage, component).getTexture();
		texture.setBoundaryModeS(Texture.WRAP);
		texture.setBoundaryModeT(Texture.WRAP);
		texture.setBoundaryColor(new Color4f(0.0f, 1.0f, 0.0f, 0.0f));		
		// Set up the texture attributes
		// could be REPLACE, BLEND or DECAL instead of MODULATE		
		TextureAttributes texAttr = new TextureAttributes();
		texAttr.setTextureMode(TextureAttributes.MODULATE);
		appearance.setTexture(texture);	    
		appearance.setTextureAttributes(texAttr);	   
		return this;
	}
	
	public SceneBuilder useColor(float R, float G, float B) {
		Color3f col = new Color3f(R,G,B);	    
		int coloringAttributes = ColoringAttributes.NICEST;
		ColoringAttributes ca = new ColoringAttributes(col, coloringAttributes);
		appearance.setColoringAttributes(ca);
		return this;
	}	
	
	public SceneBuilder useMaterial(float R, float G, float B, float rgbSpecular, float lesk) {
		useMaterial(new Color3f(R,G,B),rgbSpecular, lesk);
		return this;
	}
	
	public SceneBuilder useMaterial(Color3f color, float rgbSpecular, float lesk) {
		Color3f ambientColor = color;
		Color3f emissiveColor = new Color3f(0, 0, 0);
		Color3f diffuseColor = color;
		Color3f specularColor = new Color3f(rgbSpecular,rgbSpecular,rgbSpecular);
		Material mat = new Material(ambientColor, emissiveColor, diffuseColor, specularColor, lesk);
		appearance.setMaterial(mat);	    
		return this;
	}	
			
	public SceneBuilder resetAppearance() {
		appearance = new Appearance();
		return this;
	}
	
	public void createBackground() {
		BoundingSphere boundingSphere = new BoundingSphere(new Point3d(0.0,
				0.0, 0.0), 100.0);
		Background background = new Background(0.2f,0.2f,0.2f);
		background.setApplicationBounds(boundingSphere);
		sceneRoot.addChild(background);
	}
	
	public Group GetSceneRoot() {
		if (tgs.size() > 0)
			return tgs.getLast();
		else
			return sceneRoot;				
	}	
}
